﻿using System.Collections.Generic;
using UnityEngine;

namespace Miao
{
    /// <summary>
    /// 提供一系列物理工具
    /// </summary>
    public static class Physical
    {
        /// <summary>
        /// 获取刚体的动能
        /// </summary>
        /// <param name="rigidbody"></param>
        /// <returns></returns>
        public static float KineticEnergy(Rigidbody rigidbody)
        {
            //经典物理动能公式: Ek = (m * v^2) / 2
            return rigidbody.mass * rigidbody.velocity.sqrMagnitude * 0.5f;
        }
        /// <summary>
        /// 计算一条抛物线并返回指定精度的抛物线经过点
        /// </summary>
        /// <param name="position">抛物线在世界坐标系下的起点</param>
        /// <param name="velocity">抛物线的初速度</param>
        /// <param name="gravity">抛物线在各个轴向上受到的重力</param>
        /// <param name="sampleNumber">采样点的数量，该值越高抛物线越平滑，但计算量也更大</param>
        /// <param name="sampleDuration">表示计算抛物线在多少秒内的轨迹，该值越高抛物线越长，但计算量也更大</param>
        /// <returns>根据采样数返回的在抛物线上的点集合</returns>
        public static Vector3[] Parabola(Vector3 position, Vector3 velocity, Vector3 gravity, int sampleNumber, float sampleDuration = 2f)
        {
            float sampleInterval = sampleDuration / sampleNumber;
            Vector3[] result = new Vector3[sampleNumber];
            for (int i = 0; i < sampleNumber; i++)
            {
                float time = sampleInterval * i;
                float optimization = time * time / 2;
                Vector3 point;
                //t秒后点的位置 = 起点位置 + 在t秒后移动的距离 + 在t秒后受到重力影响而移动的距离
                //在t秒后受到重力影响而移动的距离 = 自由落体公式: 在指定时间内受到重力影响所下落的距离 = 重力 * 指定时间的平方 / 2
                point.x = position.x + velocity.x * time + gravity.x * optimization;
                point.y = position.y + velocity.y * time + gravity.y * optimization;
                point.z = position.z + velocity.z * time + gravity.z * optimization;
                result[i] = point;
            }
            return result;
        }

        public static void Parabola(Vector3 position, Vector3 velocity, Vector3 gravity, int sampleNumber, float sampleDuration, List<Vector3> result)
        {
            float sampleInterval = sampleDuration / sampleNumber;
            for (int i = 0; i < sampleNumber; i++)
            {
                float time = sampleInterval * i;
                float cache = time * time / 2;
                Vector3 point;
                //t秒后点的位置 = 起点位置 + 在t秒后移动的距离 + 在t秒后受到重力影响而移动的距离
                //在t秒后受到重力影响而移动的距离 = 自由落体公式: 在指定时间内受到重力影响所下落的距离 = 重力 * 指定时间的平方 / 2
                point.x = position.x + velocity.x * time + gravity.x * cache;
                point.y = position.y + velocity.y * time + gravity.y * cache;
                point.z = position.z + velocity.z * time + gravity.z * cache;
                result.Add(point);
            }
        }
        /// <summary>
        /// 计算在N秒内从A点到B点所需的速度(忽略空气阻力)
        /// </summary>
        /// <param name="start">在世界坐标系上的起点</param>
        /// <param name="end">在世界坐标系上的终点</param>
        /// <param name="duration">从起点到终点所需时间</param>
        /// <param name="gravity">在各个轴上受到的重力</param>
        /// <returns></returns>
        public static Vector3 Velocity(Vector3 start, Vector3 end, float duration, Vector3 gravity)
        {
            Vector3 distance = end - start;
            //首先考虑不受重力影响下的速度为 距离/时间=速度
            Vector3 velocity = distance / duration;
            //考虑重力影响:通过自由落体公式(距离=重力*时间的平方/2))可以求出在指定时间内受到重力影响的距离
            //所以受到重力影响的速度为 受到重力影响的距离/指定时间，也就是 受重力影响的速度=重力*时间*时间/2/时间。简化公式得到 速度=重力*时间/2
            float optimization = duration / 2;
            Vector3 correctedVelocity = new Vector3(velocity.x - gravity.x * optimization, velocity.y - gravity.y * optimization, velocity.z - gravity.z * optimization);
            return correctedVelocity;
        }
    }
}